ShareMenu.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. 'use client';
  2. import { useState, useRef, useEffect, useCallback } from 'react';
  3. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
  4. import { faShare, faLink, faCheck } from '@fortawesome/free-solid-svg-icons';
  5. import { faFacebook, faXTwitter } from '@fortawesome/free-brands-svg-icons';
  6. import './share-menu.scss';
  7. type Props = {
  8. title: string;
  9. };
  10. export default function ShareMenu({ title }: Props) {
  11. const [open, setOpen] = useState(false);
  12. const [copied, setCopied] = useState(false);
  13. const wrapperRef = useRef<HTMLDivElement>(null);
  14. useEffect(() => {
  15. if (!open) {
  16. return;
  17. }
  18. const handler = (e: MouseEvent) => {
  19. if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) {
  20. setOpen(false);
  21. }
  22. };
  23. document.addEventListener('mousedown', handler);
  24. return () => document.removeEventListener('mousedown', handler);
  25. }, [open]);
  26. const openShare = useCallback((target: string) => {
  27. const current = typeof window !== 'undefined' ? window.location.href : '';
  28. const url = encodeURIComponent(current);
  29. const text = encodeURIComponent(title);
  30. let shareUrl = '';
  31. switch (target) {
  32. case 'facebook':
  33. shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${url}`;
  34. break;
  35. case 'twitter':
  36. shareUrl = `https://twitter.com/intent/tweet?url=${url}&text=${text}`;
  37. break;
  38. case 'band':
  39. shareUrl = `https://band.us/plugin/share?body=${text}%20${url}`;
  40. break;
  41. default:
  42. return;
  43. }
  44. const width = 600;
  45. const height = 500;
  46. const left = window.screenX + (window.outerWidth - width) / 2;
  47. const top = window.screenY + (window.outerHeight - height) / 2;
  48. window.open(shareUrl, '_blank', `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`);
  49. setOpen(false);
  50. }, [title]);
  51. const copyLink = useCallback(async () => {
  52. try {
  53. await navigator.clipboard.writeText(window.location.href);
  54. setCopied(true);
  55. window.setTimeout(() => {
  56. setCopied(false);
  57. setOpen(false);
  58. }, 1200);
  59. } catch {
  60. setCopied(false);
  61. }
  62. }, []);
  63. return (
  64. <div className='share-menu' ref={wrapperRef}>
  65. <button
  66. type='button'
  67. className='share-menu__trigger'
  68. onClick={() => setOpen((prev) => !prev)}
  69. aria-haspopup='menu'
  70. aria-expanded={open}
  71. >
  72. <FontAwesomeIcon icon={faShare} />
  73. <span>공유</span>
  74. </button>
  75. {open && (
  76. <div className='share-menu__dropdown' role='menu'>
  77. <button type='button' onClick={copyLink} role='menuitem'>
  78. <FontAwesomeIcon icon={copied ? faCheck : faLink} />
  79. <span>{copied ? '링크가 복사되었습니다' : '링크 복사'}</span>
  80. </button>
  81. <button type='button' onClick={() => openShare('twitter')} role='menuitem'>
  82. <FontAwesomeIcon icon={faXTwitter} />
  83. <span>X (Twitter)</span>
  84. </button>
  85. <button type='button' onClick={() => openShare('facebook')} role='menuitem'>
  86. <FontAwesomeIcon icon={faFacebook} />
  87. <span>Facebook</span>
  88. </button>
  89. <button type='button' onClick={() => openShare('band')} role='menuitem'>
  90. <span aria-hidden='true' className='share-menu__band'>B</span>
  91. <span>Band</span>
  92. </button>
  93. </div>
  94. )}
  95. </div>
  96. );
  97. }